#include "child.hpp"

const int MAX_SOFT_LIMIT_RATIO = 2;

/*
 * the end operations of child process
 * para: int exitcode
 *   exitcode is used by exit()
 * return: none
 * note: has exit() inside
 */
void childProcessClose(int exitcode){
    exit(exitcode);
}

/*
 * child process, used directedly by after fork
 * para: struct structConfig mconfig: config information
 * return: none (will call childProcessClose to exit)
 * 
 * note: if execv exec OK, the return code will come from the exec file, so
 *     must ensure that THE SCRIPT WILL RETURN 0 IN THE END!!!!!(0 means exec GOOD!!!!!)
 *
 * note: child write the output.txt and error.txt by calling judgeSystem: at fact, the runtest.cpp write file
 */
void ChildProcess(struct structConfig mconfig){ 
    //set limits, including maxCpuTime, maxMemory, maxStack, maxOutputSize
    //stack: (byte)
    if (mconfig.maxStack != UNLIMITED) {
        struct rlimit ms;
        ms.rlim_cur = mconfig.maxStack;
        ms.rlim_max = mconfig.maxStack * MAX_SOFT_LIMIT_RATIO;
        if (setrlimit(RLIMIT_STACK, &ms) != 0) {
            childProcessClose(GBL_JSBC_JUDGE_CHILD_ERROR_SET_LM_STACK);
        }
    }
    //memory: (byte)
    //if (mconfig.maxMemory != UNLIMITED) {
    //    struct rlimit mas;
    //    mas.rlim_cur = mconfig.maxMemory;
    //  mas.rlim_max = UNLIMITED;
    //    if (setrlimit(RLIMIT_AS, &mas) != 0) {
    //          childProcessClose(ERRORCODE_RLIMIT_AS_CANNOT_SET);
    //    }
    //}
    //CPU time: (s)
    if (mconfig.maxCpuTime != UNLIMITED) {
		//std::cout << "I will set maxCpuTime: mconfig.maxCpuTime " << mconfig.maxCpuTime << std::endl;
        struct rlimit mc;
        mc.rlim_cur = mconfig.maxCpuTime;
        mc.rlim_max = mconfig.maxCpuTime * MAX_SOFT_LIMIT_RATIO;
        if (setrlimit(RLIMIT_CPU, &mc) != 0) {
            childProcessClose(GBL_JSBC_JUDGE_CHILD_ERROR_SET_LM_CPU);
        }
    }
    //output :(byte)
    if (mconfig.maxOutputSize != UNLIMITED) {
        struct rlimit mfs;
        mfs.rlim_cur = mconfig.maxOutputSize;
        mfs.rlim_max = mconfig.maxOutputSize * MAX_SOFT_LIMIT_RATIO;
        if (setrlimit(RLIMIT_FSIZE, &mfs) != 0) {
            childProcessClose(GBL_JSBC_JUDGE_CHILD_ERROR_SET_LM_FSIZE);
        }
    }
	
	//generate filename
	string fullOutputPath = mconfig.dir + mconfig.testID + mconfig.outputPathPos;
	string fullErrPath = mconfig.dir + mconfig.testID + mconfig.errorPathPos;
	string fullOutPath = mconfig.dir + mconfig.testID + mconfig.outPos;
	
    //redirect stdout and stderr
    //do not need to save oldout/olderr for re redirect because the process will exit(1) after excelp
    //int oldout = dup(STDOUT_FILENO);
    //int olderr = dup(STDERR_FILENO);
    int dupOutput, dupError;
    dupOutput = dupError = -1;
    dupOutput = open(fullOutputPath.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0644);
    if (dupOutput == -1) exit(GBL_JSBC_JUDGE_CHILD_ERROR_FIRST_DUP2);
    if (dup2(dupOutput, STDOUT_FILENO) == -1) exit(GBL_JSBC_JUDGE_CHILD_ERROR_FIRST_DUP2);
    dupError = open(fullErrPath.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0644);
    if (dupError == -1) exit(GBL_JSBC_JUDGE_CHILD_ERROR_SECOND_DUP2);
    if (dup2(dupError, STDERR_FILENO) == -1) exit(GBL_JSBC_JUDGE_CHILD_ERROR_SECOND_DUP2);
	
	//only if execlp err can program go straight down!
	if (mconfig.type == "0"){    //c++
		//note: cpp don't need choose para
		execlp(fullOutPath.c_str(), fullOutPath.c_str(), mconfig.testID.c_str(), mconfig.dir.c_str(), NULL);
		childProcessClose(GBL_JSBC_JUDGE_CHILD_ERROR_EXECLP);
	}
	else{   //javascript
		string javaScriptScript = mconfig.dir + mconfig.testID + usercodeFileNameMid + jsPos;
		
		//note: js need choose para
		execlp(NODEJS_PATH.c_str(), NODEJS_PATH.c_str(), javaScriptScript.c_str(), mconfig.testID.c_str(), mconfig.dir.c_str(), mconfig.choose.c_str(), NULL);
		childProcessClose(GBL_JSBC_JUDGE_CHILD_ERROR_EXECLP);
	}
}
